<template>
  <div class="course-learn" v-if="courseDetail">
    <section class="course-learn__player">
      <header class="player__header">
        <div>
          <p class="player__breadcrumb">{{ courseDetail.title }}</p>
          <h1>{{ currentLesson?.title ?? '选择章节开始学习' }}</h1>
        </div>
        <el-button text type="primary" @click="goBack">返回课程详情</el-button>
      </header>

      <div class="player__wrapper" ref="playerContainerRef">
        <video
          ref="videoRef"
          class="player__video"
          :src="currentLesson?.mediaUrl"
          preload="metadata"
          :poster="courseDetail.coverImage"
          @timeupdate="onTimeUpdate"
          @loadedmetadata="onLoadedMetadata"
          @play="onPlay"
          @pause="onPause"
          @ended="onEnded"
        ></video>
        <div class="player__controls">
          <div class="player__progress">
            <input
              type="range"
              min="0"
              max="100"
              step="0.1"
              :value="progressPercent"
              @input="onSeek"
            />
            <span class="player__time">{{ formattedCurrentTime }} / {{ formattedDuration }}</span>
          </div>
          <div class="player__toolbar">
            <button class="player__btn" type="button" @click="togglePlay">
              {{ isPlaying ? '暂停' : '播放' }}
            </button>
            <div class="player__volume">
              <button class="player__btn" type="button" @click="toggleMute">
                {{ isMuted ? '取消静音' : '静音' }}
              </button>
              <input type="range" min="0" max="1" step="0.05" :value="volume" @input="onVolumeChange" />
            </div>
            <label class="player__select">
              倍速
              <select v-model.number="playbackRate">
                <option v-for="rate in playbackRates" :key="rate" :value="rate">{{ formatRateLabel(rate) }}x</option>
              </select>
            </label>
            <button class="player__btn" type="button" @click="toggleFullScreen">
              {{ isFullscreen ? '退出全屏' : '全屏' }}
            </button>
          </div>
        </div>
        <div v-if="!currentLesson?.mediaUrl" class="player__overlay">
          <el-empty description="当前章节暂无视频资源" />
        </div>
      </div>
    </section>

    <aside class="course-learn__sidebar">
      <header class="sidebar__header">
        <h2>课程目录</h2>
        <span>学习进度 {{ overallProgress }}%</span>
      </header>
      <el-scrollbar class="sidebar__scroll">
        <el-collapse v-model="expandedSections">
          <el-collapse-item
            v-for="(section, sectionIndex) in courseDetail.outline"
            :key="section.id"
            :name="section.id"
          >
            <template #title>
              <div class="sidebar__section-title">
                <div>
                  <strong>{{ section.title }}</strong>
                  <p>{{ section.summary }}</p>
                </div>
                <el-progress
                  :percentage="getSectionProgress(section.id)"
                  :stroke-width="8"
                  :show-text="false"
                  class="sidebar__section-progress"
                />
              </div>
            </template>
            <ul class="sidebar__lessons">
              <li
                v-for="(lesson, lessonIndex) in section.lessons"
                :key="lesson.id"
                :class="[
                  'sidebar__lesson',
                  `sidebar__lesson--${getLessonStatus(lesson.id).toLowerCase()}`,
                  { 'sidebar__lesson--active': isCurrentLesson(lesson.id) },
                ]"
                role="button"
                tabindex="0"
                @click="selectLesson(sectionIndex, lessonIndex)"
                @keyup.enter="selectLesson(sectionIndex, lessonIndex)"
                @keyup.space.prevent="selectLesson(sectionIndex, lessonIndex)"
              >
                <div class="sidebar__lesson-info">
                  <span class="sidebar__lesson-title">{{ lesson.title }}</span>
                  <span class="sidebar__lesson-duration">{{ lesson.duration }}</span>
                </div>
                <span class="sidebar__lesson-status">{{ renderLessonProgress(lesson.id) }}</span>
              </li>
            </ul>
          </el-collapse-item>
        </el-collapse>
      </el-scrollbar>
    </aside>
  </div>
  <el-empty v-else description="未找到课程信息" />
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'

import { courseDetailsMock } from './course.mock'
import {
  getLessonProgress,
  setLessonProgress,
  type LessonProgressEntry,
  type LessonProgressStatus,
} from '../../api/course/progress'

const route = useRoute()
const router = useRouter()

const courseId = computed(() => route.params.courseId as string)
const courseDetail = computed(() => courseDetailsMock[courseId.value])

const sections = computed(() => courseDetail.value?.outline ?? [])

const currentSectionIndex = ref(0)
const currentLessonIndex = ref(0)

const expandedSections = ref<string[]>([])

const videoRef = ref<HTMLVideoElement | null>(null)
const playerContainerRef = ref<HTMLDivElement | null>(null)

const duration = ref(0)
const currentTime = ref(0)
const isPlaying = ref(false)
const playbackRate = ref(1)
const playbackRates = [0.75, 1, 1.5, 2]
const volume = ref(0.7)
const previousVolume = ref(0.7)
const isFullscreen = ref(false)

const lessonProgress = ref<Record<string, LessonProgressEntry>>({})

const currentSection = computed(() => sections.value[currentSectionIndex.value])
const currentLesson = computed(() => currentSection.value?.lessons[currentLessonIndex.value])

const progressPercent = computed(() => (duration.value ? (currentTime.value / duration.value) * 100 : 0))

const formattedCurrentTime = computed(() => formatTime(currentTime.value))
const formattedDuration = computed(() => formatTime(duration.value))

const overallProgress = computed(() => {
  if (!courseDetail.value) return 0
  const lessonIds = courseDetail.value.outline.reduce<string[]>((acc, section) => {
    return acc.concat(section.lessons.map((lesson) => lesson.id))
  }, [])
  if (!lessonIds.length) return 0
  const totalPercent = lessonIds.reduce((sum, lessonId) => {
    return sum + (lessonProgress.value[lessonId]?.percent ?? 0)
  }, 0)
  return Math.round(totalPercent / lessonIds.length)
})

const sectionProgressMap = computed(() => {
  if (!courseDetail.value) return {}
  const progressSnapshot = lessonProgress.value
  const map: Record<string, number> = {}
  for (const section of courseDetail.value.outline) {
    const lessonIds = section.lessons.map((lesson) => lesson.id)
    const totalPercent = lessonIds.reduce((sum, lessonId) => {
      return sum + (progressSnapshot[lessonId]?.percent ?? 0)
    }, 0)
    map[section.id] = lessonIds.length ? Math.round(totalPercent / lessonIds.length) : 0
  }
  return map
})

const getSectionProgress = (sectionId: string) => sectionProgressMap.value[sectionId] ?? 0

const getLessonStatus = (lessonId: string): LessonProgressStatus =>
  lessonProgress.value[lessonId]?.status ?? 'NOT_STARTED'

const getLessonPercent = (lessonId: string) => lessonProgress.value[lessonId]?.percent ?? 0

const renderLessonProgress = (lessonId: string) => {
  const percent = getLessonPercent(lessonId)
  const status = getLessonStatus(lessonId)
  if (status === 'COMPLETED') {
    return `已完成 ${percent}%`
  }
  if (status === 'IN_PROGRESS') {
    return `学习中 ${percent}%`
  }
  return `未开始 ${percent}%`
}

const isCurrentLesson = (lessonId: string) => currentLesson.value?.id === lessonId

const goBack = () => {
  router.push({ name: 'course-detail', params: { courseId: courseId.value } })
}

const loadProgress = () => {
  lessonProgress.value = { ...getLessonProgress(courseId.value) }
}

const clampPercent = (value: number) => {
  if (!Number.isFinite(value)) return 0
  if (value < 0) return 0
  if (value > 100) return 100
  return Math.round(value)
}

const ensureLessonInitialized = (lessonId: string) => {
  if (!lessonId) return
  if (!lessonProgress.value[lessonId]) {
    updateLessonProgress(lessonId, { status: 'NOT_STARTED', percent: 0 })
  }
}

const updateLessonProgress = (
  lessonId: string,
  patch: Partial<Omit<LessonProgressEntry, 'updatedAt'>>,
) => {
  if (!lessonId) return
  const existing = lessonProgress.value[lessonId]
  const nextPercent = clampPercent(patch.percent ?? existing?.percent ?? 0)
  const fallbackStatus = existing?.status ?? (nextPercent > 0 ? 'IN_PROGRESS' : 'NOT_STARTED')
  const nextStatus = patch.status
    ? patch.status
    : nextPercent >= 100
    ? 'COMPLETED'
    : fallbackStatus === 'NOT_STARTED' && nextPercent > 0
    ? 'IN_PROGRESS'
    : fallbackStatus
  if (existing && existing.percent === nextPercent && existing.status === nextStatus) {
    return existing
  }
  const updated = setLessonProgress(courseId.value, lessonId, {
    status: nextStatus,
    percent: nextPercent,
  })
  lessonProgress.value = { ...lessonProgress.value, [lessonId]: updated }
  return updated
}

const applyPlaybackRate = () => {
  if (!videoRef.value) return
  videoRef.value.playbackRate = playbackRate.value
}

const applyVolume = () => {
  if (!videoRef.value) return
  videoRef.value.volume = volume.value
}

const pauseVideo = () => {
  if (videoRef.value) {
    videoRef.value.pause()
  }
}

const selectLesson = async (sectionIndex: number, lessonIndex: number) => {
  currentSectionIndex.value = sectionIndex
  currentLessonIndex.value = lessonIndex
  await nextTick()
  if (videoRef.value) {
    videoRef.value.pause()
    videoRef.value.currentTime = 0
    videoRef.value.load()
  }
  currentTime.value = 0
  duration.value = 0
  isPlaying.value = false
  const lessonId = currentLesson.value?.id
  if (lessonId) {
    ensureLessonInitialized(lessonId)
  }
  applyPlaybackRate()
  applyVolume()
}

const onLoadedMetadata = () => {
  if (!videoRef.value) return
  duration.value = videoRef.value.duration || 0
  const lessonId = currentLesson.value?.id
  if (!lessonId || !duration.value) return
  const percent = getLessonPercent(lessonId)
  if (percent > 0 && percent < 100) {
    const targetTime = (percent / 100) * duration.value
    videoRef.value.currentTime = targetTime
    currentTime.value = targetTime
  }
}

const onTimeUpdate = () => {
  if (!videoRef.value) return
  currentTime.value = videoRef.value.currentTime
  const lessonId = currentLesson.value?.id
  if (!lessonId || !duration.value) return
  const percent = clampPercent((currentTime.value / duration.value) * 100)
  updateLessonProgress(lessonId, { percent })
}

const onSeek = (event: Event) => {
  if (!videoRef.value || !duration.value) return
  const target = event.target as HTMLInputElement
  const percent = clampPercent(Number(target.value))
  videoRef.value.currentTime = (percent / 100) * duration.value
  const lessonId = currentLesson.value?.id
  if (lessonId) {
    updateLessonProgress(lessonId, { percent })
  }
}

const togglePlay = () => {
  if (!videoRef.value) return
  if (!currentLesson.value?.mediaUrl) {
    ElMessage.warning('当前章节暂无视频资源')
    return
  }
  if (isPlaying.value) {
    videoRef.value.pause()
  } else {
    videoRef.value.play().catch(() => {
      ElMessage.error('无法播放视频资源')
    })
  }
}

const onPlay = () => {
  isPlaying.value = true
  const lessonId = currentLesson.value?.id
  if (!lessonId) return
  if (getLessonStatus(lessonId) === 'NOT_STARTED') {
    updateLessonProgress(lessonId, { status: 'IN_PROGRESS' })
  }
}

const onPause = () => {
  isPlaying.value = false
}

const onEnded = () => {
  isPlaying.value = false
  const lessonId = currentLesson.value?.id
  if (!lessonId) return
  updateLessonProgress(lessonId, { status: 'COMPLETED', percent: 100 })
}

const onVolumeChange = (event: Event) => {
  const value = Number((event.target as HTMLInputElement).value)
  volume.value = value
}

const toggleMute = () => {
  if (volume.value === 0) {
    volume.value = previousVolume.value || 0.7
  } else {
    previousVolume.value = volume.value
    volume.value = 0
  }
}

const toggleFullScreen = async () => {
  const container = playerContainerRef.value
  if (!container) return
  try {
    if (!document.fullscreenElement) {
      await container.requestFullscreen()
    } else {
      await document.exitFullscreen()
    }
  } catch (error) {
    ElMessage.error('无法切换全屏模式')
  }
}

const isMuted = computed(() => volume.value === 0)

const onFullscreenChange = () => {
  isFullscreen.value = !!document.fullscreenElement
}

const handleRouteLesson = () => {
  const lessonId = route.query.lesson as string | undefined
  if (!lessonId || !sections.value.length) return false
  for (let sectionIndex = 0; sectionIndex < sections.value.length; sectionIndex += 1) {
    const lessonIndex = sections.value[sectionIndex].lessons.findIndex((lesson) => lesson.id === lessonId)
    if (lessonIndex !== -1) {
      void selectLesson(sectionIndex, lessonIndex)
      return true
    }
  }
  return false
}

const formatTime = (seconds: number) => {
  if (!Number.isFinite(seconds)) return '00:00'
  const totalSeconds = Math.floor(seconds)
  const mins = Math.floor(totalSeconds / 60)
  const secs = totalSeconds % 60
  return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
}

const formatRateLabel = (rate: number) => {
  return Number.isInteger(rate) ? String(rate) : rate.toString()
}

watch(
  () => playbackRate.value,
  () => {
    applyPlaybackRate()
  },
)

watch(
  () => volume.value,
  () => {
    applyVolume()
  },
)

watch(
  () => courseDetail.value,
  (detail) => {
    if (!detail) return
    loadProgress()
    expandedSections.value = detail.outline.map((section) => section.id)
    const matched = handleRouteLesson()
    if (!matched && sections.value.length) {
      void selectLesson(0, 0)
    }
    applyPlaybackRate()
    applyVolume()
  },
  { immediate: true },
)

watch(
  () => route.query.lesson,
  () => {
    handleRouteLesson()
  },
)

onMounted(() => {
  document.addEventListener('fullscreenchange', onFullscreenChange)
})

onBeforeUnmount(() => {
  document.removeEventListener('fullscreenchange', onFullscreenChange)
  pauseVideo()
})
</script>

<style scoped>
.course-learn {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 32px;
  padding: 40px 48px 64px;
  background-color: #f3f4f8;
  min-height: 100vh;
}

.course-learn__player {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.player__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.player__breadcrumb {
  color: #6b7280;
  margin-bottom: 6px;
}

.player__header h1 {
  font-size: 26px;
  font-weight: 700;
  color: #111827;
}

.player__wrapper {
  position: relative;
  background: #000;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 25px 50px rgba(15, 23, 42, 0.25);
}

.player__video {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}

.player__controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 16px 20px;
  background: linear-gradient(180deg, rgba(17, 24, 39, 0) 0%, rgba(17, 24, 39, 0.85) 100%);
  color: #f9fafb;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.player__progress {
  display: flex;
  align-items: center;
  gap: 16px;
}

.player__progress input[type='range'] {
  flex: 1;
  appearance: none;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.35);
  cursor: pointer;
}

.player__progress input[type='range']::-webkit-slider-thumb {
  appearance: none;
  width: 12px;
  height: 12px;
  background: #3b82f6;
  border-radius: 50%;
}

.player__time {
  font-size: 12px;
  color: #d1d5db;
}

.player__toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
}

.player__btn {
  background: rgba(255, 255, 255, 0.1);
  border: none;
  color: #f9fafb;
  padding: 8px 14px;
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.2s ease;
}

.player__btn:hover {
  background: rgba(255, 255, 255, 0.2);
}

.player__volume {
  display: flex;
  align-items: center;
  gap: 12px;
}

.player__volume input[type='range'] {
  width: 120px;
  appearance: none;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.35);
}

.player__volume input[type='range']::-webkit-slider-thumb {
  appearance: none;
  width: 12px;
  height: 12px;
  background: #10b981;
  border-radius: 50%;
}

.player__select {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
}

.player__select select {
  background: rgba(255, 255, 255, 0.08);
  color: #f9fafb;
  border: none;
  padding: 6px 12px;
  border-radius: 10px;
  cursor: pointer;
}

.player__select select option {
  color: #111827;
  background-color: #ffffff;
}

.player__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(17, 24, 39, 0.65);
}

.course-learn__sidebar {
  background: #fff;
  border-radius: 24px;
  padding: 24px;
  box-shadow: 0 20px 45px rgba(15, 23, 42, 0.12);
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.sidebar__header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.sidebar__header h2 {
  font-size: 22px;
  font-weight: 700;
  color: #111827;
}

.sidebar__scroll {
  max-height: calc(100vh - 220px);
}

.sidebar__section-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}

.sidebar__section-title p {
  margin-top: 4px;
  font-size: 13px;
  color: #6b7280;
}

.sidebar__section-progress {
  width: 72px;
}

.sidebar__lessons {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.sidebar__lesson {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: 16px;
  border: 1px solid transparent;
  background: #f9fafb;
  cursor: pointer;
  transition: border 0.2s ease, background 0.2s ease;
}

.sidebar__lesson:hover,
.sidebar__lesson:focus-visible {
  border-color: rgba(59, 130, 246, 0.35);
}

.sidebar__lesson--active {
  border-color: #2563eb;
  background: rgba(37, 99, 235, 0.08);
}

.sidebar__lesson--completed {
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.4);
}

.sidebar__lesson--in_progress {
  background: rgba(59, 130, 246, 0.12);
  border-color: rgba(59, 130, 246, 0.35);
}

.sidebar__lesson-info {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.sidebar__lesson-title {
  font-weight: 600;
  color: #1f2937;
}

.sidebar__lesson-duration {
  font-size: 12px;
  color: #6b7280;
}

.sidebar__lesson-status {
  font-size: 12px;
  font-weight: 600;
  color: #2563eb;
}

@media (max-width: 1280px) {
  .course-learn {
    grid-template-columns: 1fr;
  }

  .course-learn__sidebar {
    max-height: none;
  }

  .sidebar__scroll {
    max-height: none;
  }
}

@media (max-width: 768px) {
  .course-learn {
    padding: 28px 16px 48px;
    gap: 24px;
  }

  .player__header h1 {
    font-size: 22px;
  }
}
</style>
